{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit Actions;

interface

uses Classes, AHMTypes, UIObjects, Functions, XMLHelper;

const
{$IFDEF DESIGNER}
  MODIFY_CAPTION = 'Edit Action';

  // Property categories and hints for designer
  CAT_ACTION = 'Action';
  HINT_FUNCTION = 'specifies the function that this action will call when triggered';
  HINT_TARGET = 'specifies a target for the function that this action will call';
  HINT_PARAMETER = 'specifies a parameter for the function that this action will call';
  HINT_FINAL = 'determines whether higher level actions will be triggered after this one';
{$ENDIF}

  // Action names
  AC_OKAY  = 'okay';
  AC_HOME  = 'home';
  AC_UP    = 'up';
  AC_DOWN  = 'down';
  AC_LEFT  = 'left';
  AC_RIGHT = 'right';
  AC_BACK  = 'back';
  AC_HELP  = 'help';
  AC_MENU  = 'menu';
  AC_MUTE  = 'mute';
  AC_QUIT  = 'quit';
  AC_VIDEO = 'video';
  AC_AUDIO = 'audio';
  AC_PHOTO = 'photo';
  AC_INFO  = 'info';
  AC_PLAY  = 'play';
  AC_PAUSE = 'pause';
  AC_FFWD  = 'ffwd';
  AC_RWND  = 'rwnd';
  AC_FSKIP = 'fskip';
  AC_BSKIP = 'bskip';
  AC_STOP  = 'stop';
  AC_DIAG  = 'diag';

  // Event names
  AC_SELECT       = 'onselect';
  AC_INIT         = 'oninit';
  AC_ONIDLE       = 'onidle';
  AC_FOCUS        = 'onfocus';
  AC_UNFOCUS      = 'onunfocus';
  AC_SHOW         = 'onshow';
  AC_HIDE         = 'onhide';
  AC_MOREUP       = 'onmoreup';
  AC_MOREDN       = 'onmoredown';
  AC_MORELF       = 'onmoreleft';
  AC_MORERT       = 'onmoreright';
  AC_NONEUP       = 'onnoneup';
  AC_NONEDN       = 'onnonedown';
  AC_NONELF       = 'onnoneleft';
  AC_NONERT       = 'onnoneright';
  AC_MUTED        = 'onmuted';
  AC_UNMUTED      = 'onunmuted';
  AC_ONSTOP       = 'onstop';
  AC_ONPRIPLAY    = 'onprimaryplay';
  AC_ONSECPLAY    = 'onsecondaryplay';
  AC_ONPRIPAWS    = 'onprimarypause';
  AC_ONSECPAWS    = 'onsecondarypause';
  AC_ONPRIRSUM    = 'onprimaryresume';
  AC_ONSECRSUM    = 'onsecondaryresume';
  AC_ONPRISTOP    = 'onprimarystop';
  AC_ONSECSTOP    = 'onsecondarystop';
  AC_ONERROR      = 'onerror';
  AC_ONEMPTY      = 'onempty';
  AC_NOMEDIA      = 'onnomedia';
  AC_PRIMEDIA     = 'onprimarymedia';
  AC_SECMEDIA     = 'onsecondarymedia';
  AC_FEATUREDIS   = 'onfeaturedisabled';
  AC_NEEDRESTART  = 'onneedrestart';
  AC_ONUPDATE     = 'onupdate';
  AC_ONINCREASE   = 'onincrease';
  AC_ONDECREASE   = 'ondecrease';
  AC_ONTIMERSTART = 'ontimerstart';
  AC_ONTIMERSTOP  = 'ontimerstop';
  AC_ONTIMER      = 'ontimerinterval';

type
  TAHMActionList = class;

  TAHMAction = class(TAHMUIObject)
  private
    FFunction: TAHMFunctionString;
    FTargetName: String;
    FParameter: String;
    FFinal: Boolean;
    FPersist: Boolean;
    FActionList: TAHMActionList;
  protected
    property ActionList: TAHMActionList read FActionList write FActionList;
    function GetDesignerInfo: String; override;
    procedure ClearProperties; override;
  public
    constructor Create(const Name: String); override;
    property PersistAction: Boolean read FPersist write FPersist;
    function Invoke: Boolean;
    function RenderPreview: Boolean; override;
    function ModifyActions: Integer; override;
    function Modify(Action: Integer): Boolean; override;
    function ModifyCaption(Action: Integer): String; override;
    procedure SeedObject; override;
    procedure Assign(Source: TPersistent); override;
    procedure SaveAsXML(Lines: TStrings); override;
    procedure ParseFromXML(XML: TAHMXMLHelper); override;
    procedure BuildProperties(Properties: TAHMUIPropertyList); override;
  published
    property ActionFunction: TAHMFunctionString read FFunction write FFunction;
    property TargetName: String read FTargetName write FTargetName;
    property Parameter: String read FParameter write FParameter;
    property FinalAction: Boolean read FFinal write FFinal;
  end;

  TAHMActionList = class(TAHMUIObjectList)
  private
    FSearchIndex: Integer;
    FSearchString: String;
  protected
    function GetFirstAction(const Name: String): TAHMAction;
    function GetNextAction: TAHMAction;
  public
    constructor Create; override;
    function AddAction(const InheritFrom, NewName: String): TAHMAction;
    function GetAction(const Name: String): TAHMAction;
    function InvokeAction(const Name: String): Boolean;
    function Modify: Boolean; override;
  end;


implementation

uses SysUtils, Screen, TagNames, XMLTags, {$IFDEF DESIGNER}DesignerList,
     DesignerPropsEdit,{$ENDIF} ApplicationManager, Logging, UIControls;

constructor TAHMActionList.Create;
begin
  inherited Create;

  XMLTagName := TAG_GLOBAL_ACTIONS;
  ChildClass := TAHMAction;
  DefaultSort := lsNatural;
  FSpaceElements := False;

  // Reset search in progress flag: -1 indicates index not found
  FSearchIndex := -1;
end;

function TAHMActionList.GetFirstAction(const Name: String): TAHMAction;
begin
  // Set default result - not found
  Result := nil;

  // Look first in this list and record search settings
  FSearchIndex := IndexOf(Name);
  FSearchString := Name;

  // If found then return appropriate object
  if FSearchIndex <> -1 then
    Result := TAHMAction(Objects[FSearchIndex]);
end;

function TAHMActionList.GetNextAction: TAHMAction;
begin
  // Set default result - not found
  Result := nil;

  // Check that we have started a search
  if FSearchIndex <> -1 then
  begin
    // Search through to end of local list until we find a match
    while FSearchIndex < Pred(Count) do
    begin
      Inc(FSearchIndex);
      if Objects[FSearchIndex].Name = FSearchString then
      begin
        Result := TAHMAction(Objects[FSearchIndex]);
        Exit;
      end;
    end;

    // Not found, so reset search in progress flag
    FSearchIndex := -1;
  end;
end;

function TAHMActionList.AddAction(const InheritFrom, NewName: String): TAHMAction;
begin
  Result := TAHMAction(Inherit(InheritFrom, NewName));
end;

function TAHMActionList.GetAction(const Name: String): TAHMAction;
begin
  Result := TAHMAction(GetObject(Name));
end;

function TAHMActionList.InvokeAction(const Name: String): Boolean;
var
  oAction: TAHMAction;
begin
  // Invoke any ancestor actions first
  if BaseList is TAHMActionList then
    Result := TAHMActionList(BaseList).InvokeAction(Name)
  else
    Result := False;

  // Get first action matching passed action name
  oAction := GetFirstAction(Name);
  while oAction <> nil do
  begin
    // Invoke the action. If handled and is a final action then set handled result
    if oAction.Invoke then
      if oAction.FinalAction then
        Result := True;

    // See if we have any more matching actions
    oAction := GetNextAction;
  end;
end;

function TAHMActionList.Modify: Boolean;
begin
{$IFDEF DESIGNER}
  // Edit actions with list dialog
  Result := TfrmObjectList.ShowList(Self, nil, nil);
{$ELSE}
  Result := False;
{$ENDIF}
end;


//----------------------------------------------------------------------------//
constructor TAHMAction.Create(const Name: String);
begin
  inherited Create(Name);

  XMLTagName := TAG_ACTION;
  ObjectType := otActions;
  ImageIndex := Ord(otActions);

  FPersist := True;
end;

function TAHMAction.GetDesignerInfo: String;
begin
  // Build info for designer list form
  Result := ActionFunction;
  if TargetName <> '' then Result := Result + ': ' + TargetName;
  if Parameter <> '' then Result := Result + ', ' + Parameter;
  if FinalAction then Result := Result + ' (Final)';
end;

procedure TAHMAction.ClearProperties;
begin
  inherited;

  // Clear custom action properties
  FFunction := '';
  FTargetName := '';
  FParameter := '';
  FFinal := False;
end;

function TAHMAction.Invoke: Boolean;
begin
  // Delegate action handling to global action queue
  Result := ApplicationManagerInstance.ActionManager.EnqueueAction(Self);
end;

function TAHMAction.RenderPreview: Boolean;
begin
{$IFDEF DESIGNER}
  with ScreenInstance.Viewport do
  begin
    Canvas.DesignerBackground(ViewportRect);
    Canvas.TextRect(ViewportRect, nil, 'Action Properties' + sLineBreak +
                    sLineBreak + 'Function: ' + FFunction +
                    sLineBreak + 'Target: ' + FTargetName +
                    sLineBreak + 'Parameter: ' + FParameter +
                    sLineBreak + 'Final: ' + Bool2Str(FFinal));
  end;
{$ENDIF}
  Result := True;
end;

function TAHMAction.ModifyActions: Integer;
begin
  // Supports single action to modify action
  Result := 1;
end;

function TAHMAction.Modify(Action: Integer): Boolean;
begin
{$IFDEF DESIGNER}
  // Edit action with property dialog
  Result := TfrmObjectProperties.EditObject(Self, Owner);
  if Result then DoModified;
{$ELSE}
  Result := False;
{$ENDIF}
end;

function TAHMAction.ModifyCaption(Action: Integer): String;
begin
{$IFDEF DESIGNER}
  Result := MODIFY_CAPTION;
{$ELSE}
  Result := '';
{$ENDIF}
end;

procedure TAHMAction.SeedObject;
begin
{$IFDEF DESIGNER}
  if Ancestor = nil then
    ActionFunction := FN_NONE;
{$ENDIF}
end;

procedure TAHMAction.Assign(Source: TPersistent);
begin
  inherited Assign(Source);

  with Source as TAHMAction do
  begin
    Self.ActionFunction := ActionFunction;
    Self.TargetName := TargetName;
    Self.Parameter := Parameter;
    Self.FinalAction := FinalAction;
  end;
end;

procedure TAHMAction.SaveAsXML(Lines: TStrings);
var
  oAncs: TAHMAction;
  sWork: String;
begin
  // Don't save non-persistent actions
  if not FPersist then Exit;

  // Determine which ancestor to compare against - real or empty
  oAncs := TAHMAction(ObjectToCompare);

  // We need to explicitly save comments in overridden method
  SaveXMLComments(Lines);

  // Not using BeginTag, so need to add name manually
  sWork := TGB+TAG_ACTION+SPC+ATTR_NAME+ATB + EncodeXML(Name);

  // Do we need to specify function?
  if (FFunction <> '') and (FFunction <> oAncs.ActionFunction) then
    sWork := sWork + ATN+ATTR_FUNCTION+ATB + EncodeXML(FFunction);
  // Do we need to specify target?
  if (FTargetName <> '') and (FTargetName <> oAncs.TargetName) then
    sWork := sWork + ATN+ATTR_TARGET+ATB + EncodeXML(FTargetName);
  // Do we need to specify parameter?
  if (FParameter <> '') and (FParameter <> oAncs.Parameter) then
    sWork := sWork + ATN+ATTR_PARAMETER+ATB + EncodeXML(FParameter);
  // Do we need to specify final?
  if FFinal and (FFinal <> oAncs.FinalAction) then
    sWork := sWork + ATN+ATTR_FINAL+ATB+Bool2Str(FFinal);

  // Need to redo description and inheritance here, since not using BeginTag
  if (Description <> '') and (Description <> oAncs.Description) then
    sWork := sWork + ATN+ATTR_DESC+ATB + EncodeXML(Description);
  if (Ancestor <> nil) then
    sWork := sWork + ATN+ATTR_INHERIT+ATB + EncodeXML(Ancestor.Name);

  Lines.Add(TABS[XMLIndent + 2] + sWork + ATE);
end;

procedure TAHMAction.ParseFromXML(XML: TAHMXMLHelper);
var
  sFunc, sTarget, sParam, sFinal: String;
begin
  // Get custom action attributes
  sFunc := XML.GetAttribute(ATTR_FUNCTION);
  sTarget := XML.GetAttribute(ATTR_TARGET);
  sParam := XML.GetAttribute(ATTR_PARAMETER);
  sFinal := XML.GetAttribute(ATTR_FINAL);

  // Only populate non-empty attributes (don't overwrite ancestor)
  if sFunc <> '' then ActionFunction := sFunc;
  if sTarget <> '' then TargetName := sTarget;
  if sParam <> '' then Parameter := sParam;
  if sFinal <> '' then FinalAction := Str2Bool(sFinal);
end;

procedure TAHMAction.BuildProperties(Properties: TAHMUIPropertyList);
begin
{$IFDEF DESIGNER}
  inherited;

  // Build custom action properties
  Properties.Add('Function', 'ActionFunction', CAT_ACTION, HINT_FUNCTION);
  Properties.Add('Target', 'TargetName', CAT_ACTION, HINT_TARGET);
  Properties.Add('Parameter', 'Parameter', CAT_ACTION, HINT_PARAMETER);
  Properties.Add('Final Action', 'FinalAction', CAT_ACTION, HINT_FINAL);
{$ENDIF}
end;


end.
